import {useState} from 'react';
import {useSolanaClientProvider} from './useSolanaClientProvider';
import {getAssociatedTokenAddress} from '@solana/spl-token';
import {
  ParsedInstruction,
  ParsedTransactionWithMeta,
  PartiallyDecodedInstruction,
} from '@solana/web3.js';
import {PublicKey} from '@solana/web3.js';

export type SignaturesForAddressOptions = {
  before?: string;
  until?: string;
  limit?: number;
  minContextSlot?: number;
};

export function userSolanaTransactionHistory() {
  const [loading, setLoading] = useState<boolean>(false);
  const [beforeSignature, setBeforeSignature] = useState<string | undefined>(
    undefined,
  );
  const [hasMore, setHasMore] = useState<boolean>(true);
  const {solanaClient} = useSolanaClientProvider();

  const parseSPLTransferInstruction = (
    instructions: (ParsedInstruction | PartiallyDecodedInstruction)[],
  ): ParsedInstruction => {
    const result = instructions.find(instruction => {
      const parsedInstruction = instruction as ParsedInstruction;
      if (
        parsedInstruction.program == 'spl-token' &&
        parsedInstruction.parsed?.type == 'transferChecked'
      ) {
        return instruction;
      }
    });

    return result as ParsedInstruction;
  };

  const getSPLTransferInstruction = (
    parsedTransaction: ParsedTransactionWithMeta,
  ): ParsedInstruction | PartiallyDecodedInstruction | undefined => {
    const result = parseSPLTransferInstruction(
      parsedTransaction.transaction.message.instructions,
    );
    return result;
  };

  const getSPLTokenTransferHistory = async (
    owner: string,
    mint: string,
    options: SignaturesForAddressOptions,
  ): Promise<(ParsedTransactionWithMeta | null)[]> => {
    setLoading(true);
    let _options = options;
    _options.limit = _options.limit ?? 10;
    const associatedTokenAddress = await getAssociatedTokenAddress(
      new PublicKey(mint),
      new PublicKey(owner),
    );
    const confirmedSignatureInfoList =
      await solanaClient!.getSignaturesForAddress(
        associatedTokenAddress,
        _options,
      );
    const signatures = confirmedSignatureInfoList.map(item => item.signature);
    if (signatures.length > 0) {
      setBeforeSignature(signatures[signatures.length - 1]);
      if (signatures.length < _options.limit!) {
        setHasMore(false);
      } else {
        setHasMore(true);
      }
    } else {
      setHasMore(false);
    }
    const parsedTransactions = await solanaClient!.getParsedTransactions(
      signatures,
    );

    const parsedTransferTransactions = parsedTransactions.filter(
      parsedTransaction => {
        if (parsedTransaction != null) {
          const transferInstruction =
            getSPLTransferInstruction(parsedTransaction);
          if (transferInstruction) {
            return parsedTransaction;
          }
        }
      },
    );

    setLoading(false);

    return parsedTransferTransactions;
  };

  const parseSOLTransferInstruction = (
    instructions: (ParsedInstruction | PartiallyDecodedInstruction)[],
  ): ParsedInstruction => {
    const result = instructions.find(instruction => {
      const parsedInstruction = instruction as ParsedInstruction;
      if (
        parsedInstruction.program == 'system' &&
        parsedInstruction.parsed?.type == 'transfer'
      ) {
        return instruction;
      }
    });

    return result as ParsedInstruction;
  };

  const getSOLTransferInstruction = (
    parsedTransaction: ParsedTransactionWithMeta,
  ): ParsedInstruction | PartiallyDecodedInstruction | undefined => {
    const result = parseSOLTransferInstruction(
      parsedTransaction.transaction.message.instructions,
    );
    return result;
  };

  const getSOLTransferHistory = async (
    owner: string,
    options: SignaturesForAddressOptions,
  ) => {
    setLoading(true);
    let _options = options;
    _options.limit = _options.limit ?? 10;
    const confirmedSignatureInfoList =
      await solanaClient!.getSignaturesForAddress(
        new PublicKey(owner),
        _options,
      );
    const signatures = confirmedSignatureInfoList.map(item => item.signature);
    if (signatures.length > 0) {
      setBeforeSignature(signatures[signatures.length - 1]);
      if (signatures.length < _options.limit!) {
        setHasMore(false);
      } else {
        setHasMore(true);
      }
    } else {
      setHasMore(false);
    }

    const parsedTransactions = await solanaClient!.getParsedTransactions(
      signatures,
    );

    const parsedTransferTransactions = parsedTransactions.filter(
      parsedTransaction => {
        if (parsedTransaction != null) {
          // const transferInstruction =
          //   getSOLTransferInstruction(parsedTransaction);
          // if (transferInstruction) {
          //   return parsedTransaction;
          // }
          return parsedTransaction;
        }
      },
    );
    setLoading(false);

    return parsedTransferTransactions;
  };

  const getParsedTransactionBySignature = async (signature: string) => {
    return await solanaClient!.getParsedTransaction(signature);
  };

  return {
    loading,
    beforeSignature,
    hasMore,
    getSPLTransferInstruction,
    getSOLTransferInstruction,
    getSPLTokenTransferHistory,
    getSOLTransferHistory,
    getParsedTransactionBySignature,
  };
}
